﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace ConstantCMapEditor3
{
    public class Scene
    {
        protected List<Scene> _childern = new List<Scene>();
        protected Scene _parent = null;

        public Object UserData;
     
        public delegate void MouseTouchEventHandler(MouseEvent mouseEvent, Scene sender);
        public MouseTouchEventHandler TouchEvent = null;

        protected List<Modifier> mlist = new List<Modifier>();
        /*
     public delegate void GestureEventHandler(GestureSample gesture, Scene sender);
     public GestureEventHandler GestureEvent = null;
      * */


        public Scene FindChildIsTouched(Vector2 Point) 
        {
            for (int i = _childern.Count - 1; i >= 0; i--)
            {
                if (_childern[i].IsTouch(Point))
                {
                    return _childern[i];
                }
            }
            return null;
        }

        public List<Scene> FindChildIsTouched(Vector2 LeftTop, Vector2 RightDown) 
        {
            Vector2 tempLeftTop = LeftTop;
            Vector2 tempRightDown = RightDown;

            if (tempLeftTop.X > tempRightDown.X) 
            {
                LeftTop.X = tempRightDown.X;
                RightDown.X = tempLeftTop.X;
            }

            if (tempLeftTop.Y > tempRightDown.Y)
            {
                LeftTop.Y = tempRightDown.Y;
                RightDown.Y = tempLeftTop.Y;
            }


            List<Scene> list = new List<Scene>();

            foreach(Scene s in _childern)
            {
                if (s.Position.X >= LeftTop.X && s.Position.Y >= LeftTop.Y && s.Position.X <= RightDown.X && s.Position.Y <= RightDown.Y) 
                {
                    list.Add(s);
                }
            }

            return list;
        }

        public int PressID = -1;
        protected Modifier _modifier;

        public void clearChild()
        {
            _childern.Clear();
        }

        public bool FixRotate
        {
            get;
            set;
        }

        public Scene()
        {
            Touchable = false;
            SprteEffect = SpriteEffects.None;
            _modifier = null;
            FixRotate = false;
            IsFloatPositionAllowed = false;
        }


        public void RegisterModifier(Modifier modifier)
        {
            if (mlist.Count > 9) throw new Exception("too many modifier on a scene, use complexmodifier instead.");

            addList.Add(modifier);
        }

        protected List<Modifier> trashList = new List<Modifier>();
        protected List<Modifier> addList = new List<Modifier>();


        // mlist.Add(modifier);

        public void UnregisterModifier()
        {
            mlist.Clear();
        }

        public void UnregisterModifier(Modifier modifier)
        {
            trashList.Add(modifier);
        }

        public void AttachChild(Scene child)
        {

            _childern.Add(child);
            child.Parent = this;
        }

        public void AttachChild(Scene child, int index) 
        {
            try
            {
                _childern.Insert(index, child);
                child.Parent = this;
            }
            catch 
            {
                AttachChild(child);
                Console.WriteLine("some bug happened");
            }
        }

        public int DettachChildReturnIndex(Scene child) 
        {
            int index = _childern.IndexOf(child);

            _childern.Remove(child);

            return index;
        }

        public void DettachChild(Scene child)
        {
            _childern.Remove(child);
        }

        public SpriteEffects SprteEffect
        {
            get;
            set;
        }

        public Scene Parent
        {
            get
            {
                return _parent;
            }
            set
            {
                _parent = value;
            }
        }

        public bool Touchable
        {
            get;
            set;
        }

        //templateMethod
        public void UpdateScene(GameTime time)
        {
            Update(time);

            for (int i = 0; i < _childern.Count; i++)
            {
                _childern[i].UpdateScene(time);
            }
        }

        virtual protected void Update(GameTime time)
        {
            foreach (Modifier mo in mlist)
            {
                mo.Update(time, this);
                if (mo.Finished) trashList.Add(mo);
            }

            foreach (Modifier m in trashList)
            {
                mlist.Remove(m);
            }

            foreach (Modifier m in addList)
            {
                mlist.Add(m);
            }

            trashList.Clear();
            addList.Clear();
        }

        //Root Template Method 
        
        public void ScreenOnTouch(MouseEvent mouseEvent)
        {
          RecursiveTouch(mouseEvent);
        }

        //template Method
        public bool RecursiveTouch(MouseEvent mouseEvent)
        {
            if (!Touchable) return false;
            //檢查子結點
            for (int i = _childern.Count - 1; i >= 0; i--)
            {
                if (_childern[i].RecursiveTouch(mouseEvent))
                {
                    return true;
                }
            }

            if (mouseEvent.ClickID == PressID)
            {
                if (TouchEvent != null)
                {
                    TouchEvent(mouseEvent, this);
                }
                return true;
            }

            //檢查自己
            if (IsTouch(mouseEvent.Position))
            {
                if (mouseEvent.ClickDownEdgeL || mouseEvent.ClickDownEdgeR || mouseEvent.ClickUpEdgeL || mouseEvent.ClickUpEdgeR ||mouseEvent.ClickUpEdgeM || mouseEvent.ClickDownEdgeM)
                {
                    PressID = mouseEvent.ClickID;

                    if (TouchEvent != null)
                    {
                        TouchEvent(mouseEvent, this);
                    }
                    return true;
                }
            }
            return false;


        }


        //Member Data
        protected Vector2 _position = new Vector2(0, 0);
        protected float _rotate = 0;
        protected float _scaleX = 1;
        protected float _scaleY = 1;
        private Color _color = Color.White;

        public bool IsFloatPositionAllowed
        {
            get;
            set;
        }

        //Property
        virtual public Vector2 Position
        {
            get
            {
                return _position;
            }
            set
            {
                if (!IsFloatPositionAllowed) _position = new Vector2((int)value.X, (int)value.Y);
                else _position = value;
            }
        }

        public float Rotate
        {
            get
            {
                return _rotate;
            }
            set
            {
                _rotate = value;
            }
        }
        public float Scale
        {
            get
            {
                return _scaleX;
            }
            set
            {
                _scaleX = value;
                _scaleY = value;
            }
        }

        public float ScaleX
        {
            get
            {
                return _scaleX;
            }
            set
            {
                _scaleX = value;
            }
        }

        public float ScaleY
        {
            get
            {
                return _scaleY;
            }
            set
            {
                _scaleY = value;
            }
        }

        public Color SpriteColor
        {
            get
            {
                return _color;
            }
            set
            {
                _color = value;
            }
        }

        //Absolute
        protected Vector2 _absolutePosition = new Vector2(0, 0);
        protected float _absoluteRotate = 0;
        protected float _absoluteScaleX = 1;
        protected float _absoluteScaleY = 1;
        protected Color _absoluteColor = Color.White;

        //template method
        public void RecursiveDraw(SpriteBatch batch)
        {
            RecursiveDraw(batch, new Vector2(0, 0), 0, 1, 1, Color.White);
        }

        //template method
        virtual public void RecursiveDraw(SpriteBatch batch, Vector2 parentPosition, float parentRotate, float scaleX, float scaleY, Color parentColor)
        {
            if (FixRotate)
            {
                parentRotate = 0;
            }

            _absoluteRotate = Rotate + parentRotate;
            _absoluteScaleX = _scaleX * scaleX;
            _absoluteScaleY = _scaleY * scaleY;


            double originRotate = Math.Atan(Position.Y / Position.X + 0.0001);

            if (Position.X == 0 && Position.Y == 0) originRotate = 0;
            else if (Position.X < 0) originRotate += Math.PI;

            _absolutePosition.X = scaleX * Position.Length() * (float)Math.Cos(parentRotate + originRotate) + parentPosition.X;
            _absolutePosition.Y = scaleY * Position.Length() * (float)Math.Sin(parentRotate + originRotate) + parentPosition.Y;

            AbsoluteDraw(batch, _absolutePosition, _absoluteRotate, _absoluteScaleX, _absoluteScaleY, SpriteColor);

            foreach (Scene child in _childern)
            {
                child.RecursiveDraw(batch, _absolutePosition, _absoluteRotate, _absoluteScaleX, _absoluteScaleY, _absoluteColor);
            }
        }

        virtual protected bool IsTouch(Vector2 touchLocation)
        {
            return AlwaysTouchForPureScene;
        }
        virtual protected void AbsoluteDraw(SpriteBatch batch, Vector2 position, float rotate, float scaleX, float scaleY, Color color)
        {

        }

        public bool AlwaysTouchForPureScene = false;
    }

   

    public class OffsetSprite : Sprite
    {
        public OffsetSprite(IntelligentTexture texture)
            : base(texture)
        {
        }

        public Vector2 offset
        {
            get;
            set;
        }

        override protected void AbsoluteDraw(SpriteBatch batch, Vector2 position, float rotate, float scaleX, float scaleY, Color color)
        {
            width = Width * _absoluteScaleX;
            height = Height * _absoluteScaleY;
            halfWidth = width / 2;
            halfHeight = height / 2;

            Rectangle rectangle = new Rectangle(
                (int)(_absolutePosition.X),
                (int)(_absolutePosition.Y),
                (int)(width),
                (int)(height));

            batch.Draw(Texture, rectangle, ResourceRectangle, color, rotate, new Vector2(offset.X, offset.Y), SprteEffect, 1);
        }
    }

    public class Sprite : Scene
    {
        private Texture2D _texture;

        public Sprite(IntelligentTexture texture)
        {
            _texture = texture.Texture2D;
            ResourceRectangle = texture.ResourceRectangle;
        }

        public Rectangle ResourceRectangle
        {
            get;
            set;
        }

        public Texture2D Texture
        {
            get
            {
                return _texture;
            }
            set
            {
                _texture = value;
            }
        }

        protected float width;
        protected float height;
        protected float halfWidth;
        protected float halfHeight;

        virtual public float Width
        {
            get
            {
                return ResourceRectangle.Width;
            }
        }

        virtual public float Height
        {
            get
            {
                return ResourceRectangle.Height;
            }
        }

        override protected bool IsTouch(Vector2 touchLocation)
        {

            if (Keyboard.GetState().IsKeyDown(Keys.LeftControl)) return false;
            Vector2 dif = touchLocation - _absolutePosition;
            float length = dif.Length();

            double originRotate = Math.Atan(dif.Y / dif.X + 0.0001);

            if (_absolutePosition.X == 0 && _absolutePosition.Y == 0) originRotate = 0;
            else if (_absolutePosition.X < 0) originRotate += Math.PI;


            double theta = originRotate - _absoluteRotate;
            touchLocation = new Vector2((float)(length * Math.Cos(theta)), (float)(length * Math.Sin(theta)));

            if (-halfWidth <= touchLocation.X &&
                halfWidth >= touchLocation.X &&
                 -halfHeight <= touchLocation.Y &&
                 +halfHeight >= touchLocation.Y)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        override protected void AbsoluteDraw(SpriteBatch batch, Vector2 position, float rotate, float scaleX, float scaleY, Color color)
        {
            width = Width * _absoluteScaleX;
            height = Height * _absoluteScaleY;
            halfWidth = width / 2;
            halfHeight = height / 2;

            Rectangle rectangle = new Rectangle(
                (int)(_absolutePosition.X),
                (int)(_absolutePosition.Y),
                (int)(width),
                (int)(height));


            batch.Draw(Texture, rectangle, ResourceRectangle, color, rotate, new Vector2(Width / 2, Height / 2), SprteEffect, 1);
        }
    }

    public class TextSprite : Scene
    {
        public TextSprite()
        {
            Text = "";
            Font = Game1.font1;
        }
        public TextSprite(SpriteFont font, String text)
        {
            Text = text;
            Font = font;
        }

        public SpriteFont Font
        {
            get;
            set;
        }

        public String Text
        {
            get;
            set;
        }
        protected float width;
        protected float height;
        protected float halfWidth;
        protected float halfHeight;

        virtual public float Width
        {
            get
            {
                return Font.MeasureString(Text).X;
            }
        }

        virtual public float Height
        {
            get
            {
                return Font.MeasureString(Text).Y;
            }
        }

        override protected bool IsTouch(Vector2 touchLocation)
        {
            Vector2 dif = touchLocation - _absolutePosition;
            float length = dif.Length();

            double originRotate = Math.Atan(dif.Y / dif.X + 0.0001);

            if (_absolutePosition.X == 0 && _absolutePosition.Y == 0) originRotate = 0;
            else if (_absolutePosition.X < 0) originRotate += Math.PI;


            double theta = originRotate - _absoluteRotate;
            touchLocation = new Vector2((float)(length * Math.Cos(theta)), (float)(length * Math.Sin(theta)));

            if (-halfWidth <= touchLocation.X &&
                halfWidth >= touchLocation.X &&
                 -halfHeight <= touchLocation.Y &&
                 +halfHeight >= touchLocation.Y)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        override protected void AbsoluteDraw(SpriteBatch batch, Vector2 position, float rotate, float scaleX, float scaleY, Color color)
        {
            width = Width * _absoluteScaleX;
            height = Height * _absoluteScaleY;
            halfWidth = width / 2;
            halfHeight = height / 2;

            Rectangle rectangle = new Rectangle(
                (int)(_absolutePosition.X),
                (int)(_absolutePosition.Y),
                (int)(width),
                (int)(height));

            Rectangle resourceRectangle = new Rectangle
                (
                0,
                0,
                (int)Width,
                (int)Height
                );

            Game1.spriteBatch.DrawString(Font, Text, position, color, rotate, Font.MeasureString(Text) / 2, new Vector2(_absoluteScaleX, _absoluteScaleY), SpriteEffects.None, 1);
        }
    }

    class AnimateSprite : Sprite
    {
        public List<Rectangle> _resourceRectangleList;
        float[] _durationList;
        int[] _animateIndexList;

        int singleWidth;
        int singleHeight;

        public delegate void AnimateDoneEventHandler(AnimateSprite sprite);
        public AnimateDoneEventHandler AnimateDoneEvent = null;

        public bool LoopAnimate
        {
            get;
            set;
        }

        override public float Width
        {
            get
            {
                return singleWidth;
            }
        }
        override public float Height
        {
            get
            {
                return singleHeight;
            }
        }

        float currentCount;
        int currentIndex;
        bool animating;


        public AnimateSprite(IntelligentTexture texture, Vector2 RawColumn)
            : base(texture)
        {
            LoopAnimate = true;
            _resourceRectangleList = new List<Rectangle>();

            singleWidth = (int)(texture.ResourceRectangle.Width / RawColumn.X);
            singleHeight = (int)(texture.ResourceRectangle.Height / RawColumn.Y);
            for (int j = 0; j < RawColumn.Y; j++)
            {
                for (int i = 0; i < RawColumn.X; i++)
                {
                    _resourceRectangleList.Add(new Rectangle(texture.ResourceRectangle.X + i * singleWidth, texture.ResourceRectangle.Y+j * singleHeight, singleWidth, singleHeight));
                }
            }
            int number = (int)(RawColumn.X * RawColumn.Y);

            _durationList = new float[number];
            _animateIndexList = new int[number];

            for (int i = 0; i < number; i++)
            {
                _durationList[i] = 50;
                _animateIndexList[i] = i;
            }

            currentCount = 0;
            currentIndex = 0;
            animating = false;
        }


        protected override void Update(GameTime time)
        {
            foreach (Modifier mo in mlist)
            {
                mo.Update(time, this);
                if (mo.Finished) trashList.Add(mo);
            }

            foreach (Modifier m in trashList)
            {
                mlist.Remove(m);
            }

            foreach (Modifier m in addList)
            {
                mlist.Add(m);
            }

            trashList.Clear();
            addList.Clear();

            if (animating)
            {
                currentCount += time.ElapsedGameTime.Milliseconds;

                if (currentCount > _durationList[currentIndex])
                {
                    currentCount = 0;
                    currentIndex++;
                }

                if (currentIndex >= _animateIndexList.Length)
                {
                    if (LoopAnimate)
                    {
                        currentIndex = 0;                        
                    }
                    else
                    {
                        currentIndex = _animateIndexList.Length - 1;
                        animating = false;
                        if (AnimateDoneEvent != null) AnimateDoneEvent.Invoke(this);
                    }        
                }
            }
        }

        public void Stop()
        {
            animating = false;
        }

        public void Start()
        {
            currentIndex = 0;
            currentCount = 0;
            animating = true;
        }

        public void setTile(int index)
        {
            float[] temp = { 1 };
            Start(temp, index, index);
        }

        public void Start(float[] durationList, int[] animateIndexList)
        {
            _durationList = durationList;
            _animateIndexList = animateIndexList;
            currentIndex = 0;
            currentCount = 0;
            Start();
        }

        public void Start(float[] durationList, int startIndex, int endIndex)
        {
            currentIndex = 0;
            currentCount = 0;

            _durationList = durationList;
            _animateIndexList = new int[endIndex - startIndex + 1];


            for (int i = 0; i < _animateIndexList.Length; i++) 
            {
                _animateIndexList[i] = startIndex + i;
            }
            Start();
        }

        public void Resume()
        {
            animating = true;
        }

        public void Start(float duration)
        {
            int count = _resourceRectangleList.Count;
            float[] durationList = new float[count];
            for (int i = 0; i < count; i++)
                durationList[i] = duration;

            Start(durationList, 0, count - 1);
        }

        override protected void AbsoluteDraw(SpriteBatch batch, Vector2 position, float rotate, float scaleX, float scaleY, Color color)
        {
            width = Width * _absoluteScaleX;
            height = Height * _absoluteScaleY;
            halfWidth = width / 2;
            halfHeight = height / 2;

            Rectangle rectangle = new Rectangle(
                (int)(_absolutePosition.X),
                (int)(_absolutePosition.Y),
                (int)(width),
                (int)(height));

            batch.Draw(Texture, rectangle, _resourceRectangleList[_animateIndexList[currentIndex]], color, rotate, new Vector2(Width / 2, Height / 2), SprteEffect, 1);
        }
    }
}
